﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Configuration;
using Oracle.DataAccess.Client;

namespace Generator.Test
{
    public class EdmxManager : IDisposable
    {
        private XDocument doc = null;
        private OracleConnection conn = null;
        public String ConnectionString { get; set; }
        public String InputFileName { get; set; }
        public String OutputFileName { get; set; }
        public IList<T4Model> T4Models { get; private set; }
        //public ColInfo ColumnInfo { get;private set; }

        public EdmxManager(String inputFileName)
        {
            this.InputFileName = inputFileName;
            doc = XDocument.Load(this.InputFileName);
            T4Models = new List<T4Model>();
        }

        public void InitEdmx()
        {
            XNamespace xn = "http://schemas.microsoft.com/ado/2008/09/edm";
            IEnumerable<XElement> entityTypeElements = doc.Descendants(xn + "EntityType");

            foreach (XElement entityTypeElement in entityTypeElements)
            {
                // 获取实体名称
                String entityName = entityTypeElement.Attribute("Name").Value;
                IEnumerable<XElement> propertyElements = entityTypeElement.Descendants(xn + "Property");

                String pk = GetPK(entityName);
                T4Model model = new T4Model()
                {
                    TableName = entityName,
                    EntityName = entityName,
                    Comment = GetComments(entityTypeElement), //获取实体的注释
                    Pk = pk,
                    HasPk = !string.IsNullOrEmpty(pk),
                    ColList = new List<ColInfo>()
                };

                foreach (XElement propertyElement in propertyElements)
                {
                    //var colComment = GetComments(propertyElement);
                    String propertyName = propertyElement.Attribute("Name").Value;
                    ColInfo colIn = GetColInfo(entityName, propertyName, pk);
                    // 获取属性的注释
                    colIn.Comment = GetComments(propertyElement);
                    colIn.SysType = propertyElement.Attribute("Type").Value;
                    model.ColList.Add(colIn);
                }

                T4Models.Add(model);
            }
        }

        public String GetColName(String tableName, String property)
        {
            // 获取列名
            XNamespace ad = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";

            string colName = (from e in doc.Descendants(ad + "ScalarProperty")
                              where e.Parent.Attribute("StoreEntitySet").Value == tableName
                              && e.Attribute("Name").Value == property
                              select e.Attribute("ColumnName").Value).FirstOrDefault();
            return colName;
        }

        /// <summary>
        /// 根据属性获取获取字段的信息
        /// </summary>
        public ColInfo GetColInfo(String tableName, String property, String PkName = null)
        {
            string colName = GetColName(tableName, property);

            XNamespace ad = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl";

            // xml字段信息
            var pro = (from e in doc.Descendants(ad + "Property")
                       where e.Parent.Attribute("Name").Value == tableName
                       && e.Attribute("Name").Value == colName
                       select e).FirstOrDefault();

            ColInfo ColumnInfo = new ColInfo
            {
                TableName = tableName,
                ColName = colName,
                PropertyName = property,
                NullAble = true
            };

            if (!string.IsNullOrEmpty(PkName) && colName == PkName)
            {
                ColumnInfo.IsPk = true;
            }

            foreach (var item in pro.Attributes())
            {
                if (item.Name == "Type")
                {
                    ColumnInfo.DbType = item.Value;
                }
                if (item.Name == "MaxLength")
                {
                    ColumnInfo.HasLength = true;
                    ColumnInfo.Length = Int32.Parse(item.Value);
                }
                if (item.Name == "Nullable" && item.Value.ToLower() == "false")
                {
                    ColumnInfo.NullAble = false;
                }
            }
            return ColumnInfo;
        }


        /// <summary>
        /// 获取edmx文档中实体和属性的注释
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public String GetComments(XElement element)
        {
            XNamespace xn = "http://schemas.microsoft.com/ado/2008/09/edm";
            XElement DocumentEle = element.FirstNode as XElement;
            if (DocumentEle != null && DocumentEle.Name.LocalName == "Documentation")
            {
                var tmp = DocumentEle.Descendants(xn + "Summary").FirstOrDefault();
                return tmp == null ? String.Empty : tmp.Value;
            }
            else
                return String.Empty;
        }
        /// <summary>
        /// 获取表的主键
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public String GetPK(String tableName)
        {
            XNamespace xn = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl";

            var tmp = (from e in doc.Descendants(xn + "Key").Descendants(xn + "PropertyRef")
                       where e.Parent.Parent.Attribute("Name").Value == tableName
                       select e).FirstOrDefault();
            return tmp == null ? String.Empty : tmp.Attribute("Name").Value;
        }

        /// <summary>
        /// 将注释添加进edmx文件
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="inputFileName"></param>
        /// <param name="outputFileName"></param>
        public void CreateDocumentation(String connectionString, String inputFileName, String outputFileName)
        {
            this.ConnectionString = connectionString;
            this.InputFileName = inputFileName;
            this.OutputFileName = outputFileName;
            conn = new OracleConnection(connectionString);
            conn.Open();

            //GetColInfo("DA_IMG_TYPE", "DIT_PATH");

            IEnumerable<XElement> entityTypeElements = doc.Descendants("{http://schemas.microsoft.com/ado/2008/09/edm}EntityType");

            int i = 0;
            foreach (XElement entityTypeElement in entityTypeElements)
            {
                String tableName = entityTypeElement.Attribute("Name").Value;
                IEnumerable<XElement> propertyElements = entityTypeElement.Descendants("{http://schemas.microsoft.com/ado/2008/09/edm}Property");

                Console.Clear();
                Console.WriteLine("Analyzing table {0} of {1}", i++, entityTypeElements.Count());
                Console.WriteLine(" => TableName : {0}" +
                                  "\n => property count : {1}", tableName, propertyElements.Count());

                this.AddNodeDocumentation(entityTypeElement, GetTableDocumentation(tableName));

                foreach (XElement propertyElement in propertyElements)
                {
                    String tmp = propertyElement.Attribute("Name").Value;
                    var colName = GetColName(tableName, tmp);

                    this.AddNodeDocumentation(propertyElement, GetColumnDocumentation(tableName, colName));
                }
            }

            Console.WriteLine("Writing result to {0}", this.OutputFileName);
            if (File.Exists(this.OutputFileName))
                File.Delete(this.OutputFileName);
            doc.Save(this.OutputFileName);
        }
        private void AddNodeDocumentation(XElement element, String documentation)
        {
            if (String.IsNullOrEmpty(documentation))
                return;
            element.Descendants("{http://schemas.microsoft.com/ado/2008/09/edm}Documentation").Remove();

            element.AddFirst(new XElement("{http://schemas.microsoft.com/ado/2008/09/edm}Documentation", new XElement("{http://schemas.microsoft.com/ado/2008/09/edm}Summary", documentation)));
        }

        /// <summary>
        /// 获取表的注释
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private String GetTableDocumentation(String tableName)
        {
            const String sqlStr = " SELECT nvl(COMMENTS,'null') FROM USER_TAB_COMMENTS WHERE TABLE_NAME= :TABLE_NAME ";

            OracleCommand cmd = new OracleCommand(sqlStr, conn);
            cmd.Parameters.Add(":TABLE_NAME", tableName);
            var tableComment = cmd.ExecuteScalar() as String;
            if (String.IsNullOrEmpty(tableComment))
                throw new NullReferenceException("表名不存在");
            if (tableComment.Equals("null"))
                return String.Empty;
            return tableComment;
        }

        /// <summary>
        /// 获取字段的注释
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="columnName"></param>
        /// <returns></returns>
        private String GetColumnDocumentation(String tableName, String columnName)
        {
            const String sqlStr = "SELECT nvl(COMMENTS,'null') FROM USER_COL_COMMENTS WHERE TABLE_NAME= :TABLE_NAME  AND COLUMN_NAME= :COLUMN_NAME ";

            OracleCommand cmd = new OracleCommand(sqlStr, conn);
            cmd.Parameters.Add(":TABLE_NAME", tableName);
            cmd.Parameters.Add(":COLUMN_NAME", columnName);
            String comment = cmd.ExecuteScalar() as String;
            if (String.IsNullOrEmpty(comment))
                throw new NullReferenceException(String.Format("表{0}列名{1}不存在", tableName, columnName));
            if (comment.Equals("null"))
                return String.Empty;
            return cmd.ExecuteScalar() as String;

        }

        public void Dispose()
        {
            doc = null;
            conn.Close();
            conn.Dispose();
        }
    }

    public class T4Model
    {
        public string TableName { get; set; }
        public string EntityName { get; set; }
        public string FormatEntityName { get { return EntityName.ToPo(); } }
        public bool HasPk { get; set; }
        public string Pk { get; set; }
        public string Comment { get; set; }

        public IList<ColInfo> ColList { get; set; }
    }

    public class ColInfo
    {
        private bool _nullAble = true;
        private string _sysType;

        public string PropertyName { get; set; }
        public string FormatProName { get { return PropertyName.ToPo(); } }
        public string TableName { get; set; }
        //public string TbNameToPoCo { get; set; }
        public string DbType { get; set; }
        public string SysType
        {
            get
            {
                if (_sysType.Equals("DateTime"))
                {
                    return _sysType;
                }
                else
                {
                    return _sysType.ToLower();
                };
               
            }
            set { _sysType = value; }
        }
        public string ColName { get; set; }
        public string Comment { get; set; }
        public int Length { get; set; }
        public bool HasLength { get; set; }
        public bool IsPk { get; set; }

        public bool NullAble
        {
            get { return _nullAble; }
            set { _nullAble = value; }
        }

        private string ConverToSysType(string dbtype)
        {
            switch (dbtype)
            {
                case "varchar2":
                    return "string";
                case "varchar":
                    return "string";
                case "nvarchar2":
                    return "string";
                case "nvarchar":
                    return "string";
                case "clor":
                    return "string";
                case "date":
                    return "Date";
                case "number":
                    return "int";
                default:
                    throw new ArgumentException("未知类型");
                    return "";
            }
        }
    }
}
